package com.shiehub.sdk.opengw.client;

import com.alibaba.fastjson.JSON;
import com.shiehub.sdk.opengw.dto.RequestDto;
import com.shiehub.sdk.opengw.dto.ResponseDto;
import com.shiehub.sdk.opengw.encrypt.DESedeUtil;
import com.shiehub.sdk.opengw.encrypt.EncryptUtil;
import com.shiehub.sdk.opengw.encrypt.RSAUtils;
import com.shiehub.sdk.opengw.enmu.ShieEnv;
import com.shiehub.sdk.opengw.util.DomUtils;
import com.shiehub.sdk.opengw.util.ShieHttpUtil;
import com.shiehub.sdk.opengw.util.ShieTcpUtil;
import com.shiehub.sdk.opengw.util.SoapXmlConcatUtil;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.Map;
import org.dom4j.Document;

/**
 * @Description: 加解密的工具类
 * @Author: guoleijie@shie.com.cn
 * @Since:2016-9-12
 */
public class ShieClient {

  /**
   * RSA加密方式
   */
  public static final String RSA_SIGNATURE = "RSA";
  /**
   * MD5加密方式
   */
  public static final String MD5_SIGNATURE = "MD5";
  /**
   * 代理ip的key值
   */
  public static final String PROXY_HOST = "host";
  /**
   * 代理端口的key值
   */
  public static final String PROXY_PORT = "port";
  /**
   * 代理机器用户名的key值
   */
  public static final String PROXY_USER = "user";
  /**
   * 代理机器密码的key值
   */
  public static final String PROXY_PASSWORD = "password";
  private static final String encodeing = "utf-8";
  private int connectTimeout = 50000;
  private int readTimeout = 50000;
  /**
   * 代理参数Map
   */
  private Map<String, Object> proxyMap;

  public ShieClient() {
  }

  public ShieClient(int connectTimeout, int readTimeout) {
    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;
  }

  public ShieClient(int connectTimeout, int readTimeout, Map<String, Object> proxyMap) {
    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;
    this.proxyMap = proxyMap;
  }

  @SuppressWarnings("unchecked")
  public ResponseDto send(RequestDto requestDto, Map<String, Object> bizContent,
      ShieEnv env,
      String privatekey) throws Exception {
    try {
      String publicKey = env.getPublicKey();
      String url = env.getUrl();
      //加密之前的业务报文
      String bizBeforEnc = JSON.toJSONString(bizContent);
      //对业务报文URLencoder编码
      bizBeforEnc = URLEncoder.encode(bizBeforEnc, encodeing);
      //对业务报文加密
      String encryptBiz = RSAUtils.publicEncrypt(publicKey, bizBeforEnc);
      requestDto.setBizContent(encryptBiz);
      //加密后的传输报文
      String outStr = JSON.toJSON(requestDto).toString();
      Map<String, Object> msgMap = JSON.parseObject(outStr, Map.class);
      //报文排序
      String outString = EncryptUtil.sortParm(msgMap);
      //对整个报文签名
      String sign = RSAUtils.privateKeySign(privatekey, outString);
      requestDto.setSign(sign);
      //获取加密加签后报文
      String requestMsg = JSON.toJSONString(requestDto);
      //发送POST请求得到相应报文
      System.out.println(requestMsg);
      String responseMsg = ShieHttpUtil
          .doPost(url, requestMsg, connectTimeout, readTimeout, "json", proxyMap);
      ResponseDto responseDto = JSON.parseObject(responseMsg, ResponseDto.class);
      //返回的签名
      String responseSign = responseDto.getSign();
      //对返回报文排序
      responseMsg = EncryptUtil.sortParm(JSON.parseObject(responseMsg, Map.class));
      //验签操作
      boolean verifyResult = RSAUtils.publicKeyVerify(publicKey, responseSign, responseMsg);
      //如果验签成功对响应数据进行解密
      if (true == verifyResult) {
        //揭秘前的返回业务报文
        String responseBiz = responseDto.getBizContent();
        try {
          //解密返回业务报文
          responseBiz = RSAUtils.privateDecrypt(privatekey, responseBiz);
          //URLDecoder解码
          responseBiz = URLDecoder.decode(responseBiz, encodeing);
        } catch (Exception e) {

          responseDto.setResponseCode("2006");
          responseDto.setResponseDesc("报文解密失败!");
          return responseDto;
        }
//				System.out.println("解密后的报文为----------------"+responseBiz);
        responseDto.setBizContent(responseBiz);
        return responseDto;
      } else {
        responseDto.setResponseCode("2007");
        responseDto.setResponseDesc("签名认证失败!");
        return responseDto;
      }
    } catch (Exception e) {

      throw e;
    }
  }

  @SuppressWarnings("unchecked")
  public ResponseDto send(RequestDto requestDto, Map<String, Object> bizContent, String evn, String privatekey)
      throws Exception {
    try {
      ResponseDto responseDto = new ResponseDto();
      //获得服务器配置
      ShieEnv sdkServerEnv = ShieEnv.get(evn);
      if(sdkServerEnv == null){
        responseDto.setResponseCode("5001");
        responseDto.setResponseDesc(MessageFormat.format("没有找到{0}环境对应的信息",evn));
        return responseDto;
      }
      //加密之前的业务报文
      String bizBeforEnc = JSON.toJSONString(bizContent);
      //对业务报文URLencoder编码
      bizBeforEnc = URLEncoder.encode(bizBeforEnc, encodeing);
      //对业务报文加密
      String encryptBiz = RSAUtils.publicEncrypt(sdkServerEnv.getPublicKey(), bizBeforEnc);
      requestDto.setBizContent(encryptBiz);
      //加密后的传输报文
      String outStr = JSON.toJSON(requestDto).toString();
      Map<String, Object> msgMap = JSON.parseObject(outStr, Map.class);
      //报文排序
      String outString = EncryptUtil.sortParm(msgMap);
      //对整个报文签名
      String sign = RSAUtils.privateKeySign(privatekey, outString);
      requestDto.setSign(sign);
      //获取加密加签后报文
      String requestMsg = JSON.toJSONString(requestDto);
      //发送POST请求得到相应报文
      String responseMsg = ShieHttpUtil.doPost(sdkServerEnv.getUrl(), requestMsg, connectTimeout, readTimeout,
          "json", proxyMap);
       responseDto = JSON.parseObject(responseMsg, ResponseDto.class);
      //返回的签名
      String responseSign = responseDto.getSign();
      //对返回报文排序
      responseMsg = EncryptUtil.sortParm(JSON.parseObject(responseMsg, Map.class));
      //验签操作
      boolean verifyResult = RSAUtils.publicKeyVerify(sdkServerEnv.getPublicKey(), responseSign, responseMsg);
      //如果验签成功对响应数据进行解密
      if (true == verifyResult) {
        //揭秘前的返回业务报文
        String responseBiz = responseDto.getBizContent();
        try {
          //解密返回业务报文
          responseBiz = RSAUtils.privateDecrypt(privatekey, responseBiz);
          //URLDecoder解码
          responseBiz = URLDecoder.decode(responseBiz, encodeing);
        } catch (Exception e) {
          responseDto.setResponseCode("2006");
          responseDto.setResponseDesc("报文解密失败!");
          return responseDto;
        }
        //				System.out.println("解密后的报文为----------------"+responseBiz);
        responseDto.setBizContent(responseBiz);
        return responseDto;
      } else {
        responseDto.setResponseCode("2007");
        responseDto.setResponseDesc("签名认证失败!");
        return responseDto;
      }
    } catch (Exception e) {

      throw e;
    }
  }

  /**
   * @Description: （1）请求报文加签加密（2）请求webservice服务（3）响应报文解密验签
   * @param: requestXml 请求报文
   * @param: evn 环境信息
   * @param: privatekey 私钥
   * @param: signtype 签名类型
   * @ReturnType: String
   * @exception:
   * @Author: xiajiabiao@sinosoft.com.cn
   * @Since: 创建时间 2016年12月12日 上午12:28:23
   */
  @SuppressWarnings("unchecked")
  public String send(String requestXml, String evn, String privatekey, String signType) throws Exception {
    try {
      String url = ShieEnv.get(evn).getUrl();
      //对Body签名
      String requestMsg = null;
      String responseMsg = null;
      requestMsg = signXml(privatekey, requestXml, signType);
      //发送POST请求得到相应报文
      if (ShieEnv.get(evn).getEnvCode().contains("wbs")) {
        requestMsg = SoapXmlConcatUtil.concat(requestMsg);
        responseMsg = ShieHttpUtil.doPost(url, requestMsg, connectTimeout, readTimeout, "xml", proxyMap);
        //返回的签名
        responseMsg = DomUtils.getLeafString(responseMsg, "/soap:Envelope/soap:Body");//格式
        responseMsg = responseMsg.replace("&gt;", ">");
        responseMsg = responseMsg.replace("&lt;", "<");
      } else {
        responseMsg = ShieTcpUtil.sendTcp(url, requestMsg);
      }

      boolean verifyResult = false;
      verifyResult = verifyXml(privatekey, responseMsg, signType);
      //获取返回报文待验签字符串
      if (true == verifyResult) {
        return responseMsg;
      } else {
        String returnMsg = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + "<data> "
            + "<head>     " + "<sign></sign> " + "<resultCode>2007</resultCode>"
            + "<resultInfoDesc>签名认证失败!</resultInfoDesc>" + "</head> " + "<body>" + "</body> " + "</data> ";
        return returnMsg;
      }
    } catch (Exception e) {
      throw e;
    }
  }


  /**
   * @Description: （1）请求报文加签加密（2）请求webservice服务（3）响应报文解密验签
   * @param: requestXml 请求报文
   * @param: evn 环境信息
   * @param: privatekey 私钥
   * @param: signtype 签名类型
   * @ReturnType: String
   * @exception:
   * @Author: guoleijie@shie.com.cn
   * @Since: 创建时间 2016年12月12日 上午12:28:23
   */
  @SuppressWarnings("unchecked")
  public String send(String requestXml, ShieEnv env, String privatekey, String signType)
      throws Exception {
    try {
      String url = env.getUrl();
      //对Body签名
      String requestMsg = null;
      String responseMsg = null;
      requestMsg = signXml(privatekey, requestXml, signType);
      //发送POST请求得到相应报文
      if (env.getEnvCode().contains("wbs")) {
        requestMsg = SoapXmlConcatUtil.concat(requestMsg);
        responseMsg = ShieHttpUtil
            .doPost(url, requestMsg, connectTimeout, readTimeout, "xml", proxyMap);
        //返回的签名
        responseMsg = DomUtils.getLeafString(responseMsg, "/soap:Envelope/soap:Body");//格式
        responseMsg = responseMsg.replace("&gt;", ">");
        responseMsg = responseMsg.replace("&lt;", "<");
      } else {
        responseMsg = ShieTcpUtil.sendTcp(url, requestMsg);
      }

      boolean verifyResult = false;
      verifyResult = verifyXml(privatekey, responseMsg, signType);
      //获取返回报文待验签字符串
      if (true == verifyResult) {
        return responseMsg;
      } else {
        String returnMsg = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
            + "<data> "
            + "<head>     "
            + "<sign></sign> "
            + "<resultCode>2007</resultCode>"
            + "<resultInfoDesc>签名认证失败!</resultInfoDesc>"
            + "</head> "
            + "<body>"
            + "</body> "
            + "</data> ";
        return returnMsg;
      }
    } catch (Exception e) {

      throw e;
    }
  }


  /**
   * @Description: MD5和3DES加签
   * @param: strPrivateKey 3DES加签私钥
   * @param: requestXml 待加签报文
   * @param: signType 加签类型
   * @ReturnType: String
   * @exception:
   * @Author: guoleijie@shie.com.cn
   * @Since: 创建时间 2016年12月12日 下午2:03:07
   */
  @SuppressWarnings("unchecked")
  public String signXml(String strPrivateKey, String requestXml, String signType) throws Exception {
    try {
      if (MD5_SIGNATURE.equals(signType)) {
        //待加签内容
        String strPlainVal = DomUtils.getLeafString(requestXml, "/data/body");
        //签值
        String strSign = DESedeUtil.privateKeySign(strPrivateKey, strPlainVal);
        Document requestDom = DomUtils.getDocument(requestXml);
        //将签值存放报文sign节点中
        DomUtils.setSingleElementText(requestDom, "/data/head/sign", strSign);
        return requestDom.asXML();
      } else {
        //待加签内容
        String strPlainVal = DomUtils.getLeafString(requestXml, "/data/body");
        //签值
        String strSign = RSAUtils.privateKeySign(strPrivateKey, strPlainVal);
        Document requestDom = DomUtils.getDocument(requestXml);
        //将签值存放报文sign节点中
        DomUtils.setSingleElementText(requestDom, "/data/head/sign", strSign);
        return requestDom.asXML();
      }
    } catch (Exception e) {

      throw e;
    }
  }

  /**
   * @Description: MD5和3DES验签
   * @param: strKey 3DES验签私钥,RSA公钥
   * @param: responseXml 待验签报文
   * @ReturnType: boolean
   * @exception:
   * @Author: guoleijie@shie.com.cn
   * @Since: 创建时间 2016年12月12日 下午2:08:23
   */
  @SuppressWarnings("unchecked")
  public boolean verifyXml(String strKey, String responseXml, String signType) throws Exception {
    try {
      if (MD5_SIGNATURE.equals(signType)) {
        Document responseDom = DomUtils.getDocument(responseXml);
        //获得报文签值
        String strSignVal = DomUtils.getSingleElementText(responseDom, "/data/head/sign");
        //待加签内容
        String strVerifyVal = DomUtils.getLeafString(responseXml, "/data/body");
        //验签
        boolean verifyResult = DESedeUtil.privateKeyVerify(strKey, strSignVal, strVerifyVal);
        return verifyResult;
      } else {
        Document responseDom = DomUtils.getDocument(responseXml);
        String strSignVal = DomUtils.getSingleElementText(responseDom, "/data/head/sign");
        String strVerifyVal = DomUtils.getLeafString(responseXml, "/data/body");
        boolean verifyResult = RSAUtils.publicKeyVerify(strKey, strSignVal, strVerifyVal);
        return verifyResult;
      }
    } catch (Exception e) {

      throw e;
    }
  }

}
